<?php
/**
 * Created by PhpStorm.
 * User: hans
 * Date: 16/6/2
 * Time: 下午1:51.
 */
namespace App\Http\Controllers;

use GuzzleHttp\Client;
use Dingo\Api\Http\Request;
use GuzzleHttp\Exception\ClientException;

/**
 * Class AuthController.
 *
 * 获取、销毁令牌
 */
class AuthController extends BaseController
{
    /**
     * 令牌有效期,以秒为单位.
     *
     * @var int
     */
    protected $ttl = 3600 * 24 * 7;


    /**
     * @var Client
     */
    protected $client;

    /**
     * AuthController constructor.
     */
    public function __construct()
    {
        $this->client = new Client();
    }

    /**
     * 获取令牌.
     *
     * 使用用户身份信息换取令牌.
     *
     * @Post("/auth/grant")
     * @Parameters({
     *     @Parameter("account", type="string", required=true, description="账号(用户名|手机号码|电子邮箱)"),
     *     @Parameter("password", type="string", required=true, description="登录密码")
     *})
     * @Transaction({
     *     @Response(200, body={"token":"osaijfosjeieoorusefsfadjfsfs"}),
     *     @Response(401, body={"message":"授权失败", "status_code":401})
     * })
     * @Versions({"v1"})
     *
     * @param Request $request
     *
     * @return mixed
     */
    public function grant(Request $request)
    {
        $username = trim($request->input('username'));
        $password = trim($request->input('password'));

        $data = $this->getRemoteUser($username, $password);
        if ($data == false) {
            $this->response()->errorBadRequest('用户名或密码不正确');
        }

        $token = $this->generateToken();
        $this->bindToken($data['user']['id'], $token);

        return $this->response->array(['token' => $token]);
    }

    /**
     * 销毁令牌.
     *
     * 使一个现有令牌失效.
     *
     * //no test
     *
     * @Get("/auth/revoke")
     *
     * @param Request $request
     *
     * @return mixed
     */
    public function revoke(Request $request)
    {
        $token = $this->getTokenFromHeader($request);

        $this->unbindTokenByKey($token);

        return $this->response->array(['message' => '令牌已回收, token: '.$token]);
    }

    /**
     * 生成令牌.
     *
     * //no test
     *
     * @param null $salt
     *
     * @return string
     */
    protected function generateToken($salt = null)
    {
        $salt = $salt == null ? rand(100000, 999999) : $salt;
        $hash = app('hash')->make($salt.uniqid());

        return md5($hash);
    }

    /**
     * 绑定令牌和用户.
     *
     * //no test
     *
     * @param $userId
     * @param $token
     */
    protected function bindToken($userId, $token)
    {
        $singleOnline = env('SINGLE_ONLINE', true);

        if ($singleOnline) {
            $this->unbindTokenByUserId($userId);
        }

        app('redis')->setex('token:all:'.$token, $this->ttl, $userId);
        app('redis')->setex('token:app:'.$userId.':'.$token, $this->ttl, $userId);
    }

    /**
     * 回收用户的所有令牌.
     *
     * //no test
     *
     * @param $userId
     */
    protected function unbindTokenByUserId($userId)
    {
        $keys = app('redis')->keys('token:app:'.$userId.':*');
        foreach ($keys as $key) {
            $tokenArray = explode(':', $key);
            $token = end($tokenArray);

            app('redis')->del('token:all:'.$token);
            app('redis')->del($key);
        }
    }

    /**
     * 回收指定的令牌.
     *
     * //no test
     *
     * @param $token
     */
    protected function unbindTokenByKey($token)
    {
        $userId = app('redis')->get('token:all:'.$token);

        app('redis')->del('token:app:'.$userId.':'.$token);
        app('redis')->del('token:all:'.$token);
    }

    /**
     * 验证用户登录信息.
     *
     * //no test
     *
     * @param $username
     * @param $password
     *
     * @return bool|mixed
     */
    protected function getRemoteUser($username, $password)
    {
        try {
            $res = $this->client->request('POST', 'http://service-user.i.txdev.org/user/verify', [
                'query' => [
                    'username' => $username,
                    'password' => $password,
                ],
            ]);

            //Grant token
            if (200 == $res->getStatusCode()) {
                $response = \GuzzleHttp\json_decode($res->getBody(), true);
            }
        } catch (ClientException $e) {
            $response = false;
        }

        return $response;
    }
}
